#include "appinc.h"
#include "sans_demo.h"

//该文件实现了平台操作设备的系统文件，同时模拟了自定义日志文件
//demo代码没有做异常处理和权限控制

//日志列表
const char *log_list = TOSTR(
{
	"system.log":{
		"file": "/var/log/system.log"
	}, 
	"app.log": {
		"file": "/var/log/app.log"
	}
}
);

char system_log[4096] = "this is demo system log";
char app_log[4096] = "this is app log";


//open和close主要用于流操作，demo不实现
static int sans_file_open(void *obj, ssiot_message_t *msg, cJSON *data)
{
	msg->resp_code = SSCODE_ERR_REQUEST;
	msg->resp_msg = "not support";
	return -EFAULT;
}


static int sans_file_close(void *obj, ssiot_message_t *msg, cJSON *data)
{
	msg->resp_code = SSCODE_ERR_REQUEST;
	msg->resp_msg = "not support";
	return -EFAULT;
}


//平台获取设备的文件信息,system.log和app.log是自定义文件，其它是系统文件
static int sans_file_get(void *obj, ssiot_message_t *msg, cJSON *data)
{
	cJSON *file = cJSON_GetObjectItem(data, "file");
	char *path;
	struct stat st;

	if (file == NULL || file->valuestring == NULL) {
		msg->resp_code = SSCODE_ERR_REQUEST;
		msg->resp_msg = "not support";
		return -EINVAL;
	}
	path = file->valuestring;

	file = cJSON_CreateObject();
	if (stat(path, &st) == 0) {
		cJSON_AddNumberToObject(file, "size", st.st_size);
		cJSON_AddNumberToObject(file, "modify", st.st_mtime);
		cJSON_AddStringToObject(file, "method", "hex");
	} else if (!strcmp(path, "system.log")) {
		cJSON_AddStringToObject(file, "file", "/var/log/system.log");
		cJSON_AddNumberToObject(file, "size", strlen(system_log));
		cJSON_AddStringToObject(file, "method", "string");
	} else if (!strcmp(path, "app.log")) {
		cJSON_AddStringToObject(file, "file", "/var/log/app.log");
		cJSON_AddNumberToObject(file, "size", strlen(app_log));
		cJSON_AddStringToObject(file, "method", "string");
	} else {
		cJSON_Delete(file);
		msg->resp_code = SSCODE_ERR_FOUND;
		msg->resp_msg = "not found";
		return 0;
	}
	data = cJSON_CreateObject();
	cJSON_AddItemToObject(data, path, file);

	msg->resp_code = SSCODE_OK;
	msg->resp_data = data;

	return 0;
}

//平台读设备的文件
static int sans_file_read(void *obj, ssiot_message_t *msg, cJSON *data)
{
	cJSON *file = cJSON_GetObjectItem(data, "file");
	char *path, *method = "hex";
	cJSON *tmp;

	if (file == NULL || file->valuestring == NULL) {
		msg->resp_code = SSCODE_ERR_REQUEST;
		msg->resp_msg = "not support";
		return -1;
	}
	path = file->valuestring;
	tmp = cJSON_GetObjectItem(data, "method");
	if (tmp && tmp->valuestring)
		method = tmp->valuestring;

	//demo没有实现offset和count
	if (!access(path, F_OK) && !strcmp(method, "hex")) {
		long size = libfile_get_size(path);
		uint8_t *hex = malloc(size);
		char *str = malloc(size*2+1);
		libfile_read_data(path, hex, size);
		libstr_hex_to_str(hex, size, str);
		data = cJSON_CreateString(str);
		free(str);
		free(hex);
	} else if (!strcmp(path, "log.list") && !strcmp(method, "string")) {
		char buf[1024];
		sprintf(buf, TOSTR({
			"system.log":{"file": "/var/log/system.log", "size":%d}, 
			"app.log": {"file": "/var/log/app.log", "size":%d}
		}), (int)strlen(system_log), (int)strlen(app_log));
		data = cJSON_CreateString(buf);
	} else if (!strcmp(path, "/var/log/system.log") && !strcmp(method, "string")) {
		data = cJSON_CreateString(system_log);
	} else if (!strcmp(path, "/var/log/app.log") && !strcmp(method, "string")) {
		data = cJSON_CreateString(app_log);
	} else {
		msg->resp_code = SSCODE_ERR_FOUND;
		msg->resp_msg = "not found";
		return 0;
	}

	msg->resp_code = SSCODE_OK;
	msg->resp_data = data;

	return 0;
}

//平台写设备的文件
static int sans_file_write(void *obj, ssiot_message_t *msg, cJSON *data)
{
	cJSON *file = cJSON_GetObjectItem(data, "file");
	cJSON *content = cJSON_GetObjectItem(data, "content");
	char *path;
	uint8_t *hex;
	int size, ret;

	if (file == NULL || file->valuestring == NULL || content == NULL || content->valuestring == NULL) {
		msg->resp_code = SSCODE_ERR_REQUEST;
		msg->resp_msg = "not support";
		return -1;
	}
	path = file->valuestring;
	size = strlen(content->valuestring)/2;
	
	//demo没有实现offset,只实现hex方式
	hex = malloc(size);
	libstr_str_to_hex(content->valuestring, hex);
	ret = libfile_write_data(path, hex, size);
	free(hex);

	msg->resp_code = (ret == size ? SSCODE_OK : SSCODE_ERR_SERVER);

	return 0;
}



static LIST_HEAD(download_head);
static pthread_mutex_t download_mutex = PTHREAD_MUTEX_INITIALIZER;

static int get_unique_sid(void)
{
	static uint32_t sid;
	int ret;

	pthread_mutex_lock(&download_mutex);
	if (sid == 0)
		sid = libsys_get_uptime_ms()%1000;
	ret = sid++;
	pthread_mutex_unlock(&download_mutex);

	return ret;
}

//timeout:下载文件需要的总超时时间
int sans_file_download(char *file, uint8_t *buf, int size, int timeout)
{
	struct download_t *download;
	cJSON *data = cJSON_CreateObject();
	uint32_t end = 0;
	int ret, wait = 0, retry = 0;

	pthread_mutex_lock(&download_mutex);
	download = malloc(sizeof(*download));
	memset(download, 0, sizeof(*download));
	libnotify_init(&download->event);
	list_add_tail(&download->list, &download_head);
	pthread_mutex_unlock(&download_mutex);
	download->buf = buf;

	if (timeout > 0)
		end = libsys_get_uptime() + timeout;
	cJSON_AddStringToObject(data, "file", file);
	while (size > 0) {
		if (timeout > 0) {
			wait = end - libsys_get_uptime();
			if (wait <= 0) {
				ret = -ETIMEDOUT;
				break;
			}
		}
		//单次最多等待10s
		if (wait > 10 || wait == 0)
			wait = 10;
		download->sid = get_unique_sid();
		download->count = (size > READ_PKG_SIZE) ? READ_PKG_SIZE : size;
		json_add(data, "offset", cJSON_CreateNumber(download->offset));
		json_add(data, "count", cJSON_CreateNumber(download->count));
		ret = ssiot_send_data(NULL, NULL, download->sid, "file/read", data);
		if (ret < 0)
			break;
		ret = libnotify_wait(&download->event, wait*1000);
		if (ret != 0) {
			//单个分片最多请求3次
			if (++retry >= 3) {
				ret = -ETIMEDOUT;
				break;
			}
			continue;
		}
		download->offset += download->count;
		size -= download->count;
		retry = 0;
	}

	pthread_mutex_lock(&download_mutex);
	list_del(&download->list);
	pthread_mutex_unlock(&download_mutex);

	free(data);
	free(download);

	return ret;
}

int sans_file_download_ack(void *obj, ssiot_message_t *msg, cJSON *data)
{
	struct download_t *download;

	if (data == NULL || data->valuestring == NULL)
		return -1;

	pthread_mutex_lock(&download_mutex);
	list_for_each_entry(download, &download_head, list) {
		if (download->sid == msg->sid && download->count == strlen(data->valuestring) / 2) {
			libstr_str_to_hex(data->valuestring, download->buf + download->offset);
			libnotify_wakeup(&download->event);
		}
	}
	pthread_mutex_unlock(&download_mutex);

	return 0;
}


int sans_file(void *obj, const char *topic, ssiot_message_t *msg)
{
	if (msg->data == NULL) {
		msg->resp_code = SSCODE_ERR_REQUEST;
		return -EFAULT;
	}

	if (!strcmp(topic, "open"))
		return sans_file_open(obj, msg, msg->data);
	else if (!strcmp(topic, "read"))
		return sans_file_read(obj, msg, msg->data);
	else if (!strcmp(topic, "write"))
		return sans_file_write(obj, msg, msg->data);
	else if (!strcmp(topic, "close"))
		return sans_file_close(obj, msg, msg->data);
	else if (!strcmp(topic, "get"))
		return sans_file_get(obj, msg, msg->data);
	else if (!strcmp(topic, "read/ack"))
		return sans_file_download_ack(obj, msg, msg->data);

	return -1;
}

